/*
This codefile contains the functionality of the "HardwareOut" 
component. This component has not been specified in the design
report. This code manages a collection of countdown timers. 
They can be used to check when a given period of time has
passed. Running timers have a unique identifier. Any number of
timers can be used simultaneously, as long as they have a unique
ID. There is no difference between a timer that has run out and
a timer that has never been started.

The microcontroller's Timebase timer is used to generate
interrupts at a consistent interval. At each interrupt, every
running timer is counted down by the length of the interval.

Author:			Daan
Version: 		1
Last modified:	14-05-11
*/

/* interface/facade implementation */
void CountdownTimer_StartTimer (int timerId, float time)
{
	StartTimer(timerId, time);
}

BOOL CountdownTimer_HasTimerRunOutYet (int timerId)
{
	return HasTimerRunOutYet(timerId);
}



struct Timer {
	int id; /* the timer's unique identifier, used to look it up */
	float start; /* the starting value of the timer. this value is currently not used for anything */
	float current; /* the current value of the timer. the timer runs out when this reaches 0. */
	struct Timer *next; /* this is a linked list, this points to the next struct */
};



/* global variables for this component */
const float TIME_STEP = 0.1311f; /* this is the interval in seconds, with which timers are updated */
struct Timer timerListHeader; /* a pointer to the first timer in the linked list */
struct Timer timerBuffer[8]; /* buffer to allocate the linked list to */


/* initializing function for this component */
void CountdownTimer_Initialize ()
{
	/* initialize global variables */
	timerListHeader.id = -1;
	timerListHeader.start = 0.0f;
	timerListHeader.current = 0.0f;
	timerListHeader.next = NULL;
	
	/* configure the TimeBase timer's Interval Timer Function to an interval of 2^19/HCLK (Approx. 131.1ms). */
	TBTC_RESV = ON; /* manual says to set this bit. Okay.. */
	TBTC_TBIE = ON; /* enable interrupt requests */
	TBTC_TBOF = OFF; /* clear the interrupt request flag bit */
	TBTC_TBC = 0x3; /* set the interval time */
	TBTC_TBR = OFF; /* clear the timer */
}


/*
This is supposed to be the function that is called on every interrupt.
*/
void timer_isr ()
{
	UpdateTimers();
	TBTC_TBOF = OFF;
}

/*
This routine runs every time the interval timer fires an interrupt. This counts down every timer with the set interval (TIME_STEP). If a timer has run out, it is reset.
*/
void UpdateTimers ()
{
	struct Timer *currentTimer = timerListHeader.next;
	struct Timer *previousTimer = &timerListHeader;
	struct Timer *timerToBeDeleted = NULL;
	while (currentTimer != NULL)
	{
		(*currentTimer).current -= TIME_STEP;
		if ((*currentTimer).current <= 0.0f)
		{
			(*previousTimer).next = (*currentTimer).next;
			timerToBeDeleted = currentTimer;
			currentTimer = (*currentTimer).next;
			free(timerToBeDeleted);
		}
		else
		{
			previousTimer = currentTimer;
			currentTimer = (*currentTimer).next;
		}
	}
}

/*
Create a new countdown timer.
	timerId			A unique identifier for the timer, so that it can be looked up later.
	time			The length of time in seconds before it should run out.
*/
void StartTimer (int timerId, float time)
{
	/* walk through the list of timers. if a timer with the same ID exists, reset it to the new value. otherwise make a new one and add it to the end of the linked list */
	struct Timer *currentTimer = timerListHeader.next;
	struct Timer *previousTimer = &timerListHeader;
	while (currentTimer != NULL)
	{
		if ((*currentTimer).id == timerId)
		{
			(*currentTimer).start = time;
			(*currentTimer).current = time;
			return;
		}
		previousTimer = currentTimer;
		currentTimer = (*currentTimer).next;
	}
	
	/* there is no timer with the specified timerId yet. let's make one */
	(*previousTimer).next = (struct Timer *) (malloc(sizeof(struct Timer)));
	(*(*previousTimer).next).id = timerId;
	(*(*previousTimer).next).start = time;
	(*(*previousTimer).next).current = time;
	(*(*previousTimer).next).next = NULL;
}

/*
check if a timer with the given 'id' still exists. If it doesn't exist, it means it has run out.
	timerId			The unique identifier for the timer.
*/
BOOL HasTimerRunOutYet (int timerId)
{
	/* check if a timer with the given 'id' still exists. If it doesn't exist, it means it has run out */
	struct Timer *currentTimer = timerListHeader.next;
	struct Timer *previousTimer = &timerListHeader;
	while (currentTimer != NULL)
	{
		if ((*currentTimer).id == timerId)
		{
			return FALSE;
		}
		previousTimer = currentTimer;
		currentTimer = (*currentTimer).next;
	}
	return TRUE;
}